home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-05-19 | 89.8 KB | 2,843 lines |
- ;;; -*- Package: KERNEL; Log: C.Log -*-
- ;;;
- ;;; **********************************************************************
- ;;; This code was written as part of the CMU Common Lisp project at
- ;;; Carnegie Mellon University, and has been placed in the public domain.
- ;;; If you want to use this code or any part of CMU Common Lisp, please contact
- ;;; Scott Fahlman or slisp-group@cs.cmu.edu.
- ;;;
- (ext:file-comment
- "$Header: type.lisp,v 1.41 92/04/17 00:10:26 wlott Exp $")
- ;;;
- ;;; **********************************************************************
- ;;;
- ;;; This file contains definitions and utilities for the manipulation of
- ;;; Common Lisp types. The main purpose of this code is to manage a
- ;;; representation for types which is more useful than Common Lisp type
- ;;; specifiers.
- ;;;
- ;;; Written by Rob MacLachlan
- ;;;
- (in-package "KERNEL")
- (use-package "ALIEN-INTERNALS")
-
- (export '(function-type-nargs code-component code-component-p lra lra-p))
- (export '(make-alien-type-type alien-type-type
- alien-type-type-p alien-type-type-alien-type))
- (import 'c-call:void)
- (export 'void)
-
- (in-package "EXTENSIONS")
- (export 'constant-argument)
-
- (in-package "KERNEL")
-
-
- ;;; ### Remaining incorrectnesses:
- ;;;
- ;;; Type-Union (and the OR type) doesn't properly canonicalize an exhaustive
- ;;; partition or coalesce contiguous ranges of numeric types.
- ;;;
- ;;; There are all sorts of nasty problems with open bounds on float types (and
- ;;; probably float types in general.)
- ;;;
- ;;; ratio and bignum are not recognized as numeric types.
-
-
- ;;; *Use-Implementation-Types* is a semi-public flag which determines how
- ;;; restrictive we are in determining type membership. If two types are the
- ;;; same in the implementation, then we will consider them them the same when
- ;;; this switch is on. When it is off, we try to be as restrictive as the
- ;;; language allows, allowing us to detect more errors. Currently, this only
- ;;; affects array types. Types such as the float types which may be made
- ;;; synonyms would be a good target also.
- ;;;
- (defvar *use-implementation-types*)
- (proclaim '(type boolean *use-implementation-types*))
-
- ;;; These are the Common Lisp defined type specifier symbols. These are the
- ;;; things which can be used as declarations without requiring the use of TYPE.
- (defconstant type-specifier-symbols
- '(array atom bignum bit bit-vector character common compiled-function
- complex cons double-float extended-char fixnum float function
- hash-table integer keyword list long-float nil null number package
- pathname random-state ratio rational real readtable sequence
- short-float simple-array simple-bit-vector simple-string simple-vector
- single-float standard-char stream string base-char symbol t vector))
-
-
- ;;; Def-Type-Translator -- Internal
- ;;;
- ;;; Define the translation from a type-specifier to a type structure for
- ;;; some particular type. Syntax is identical to DEFTYPE.
- ;;;
- (defmacro def-type-translator (name arglist &body body)
- (check-type name symbol)
- (let ((whole (gensym)))
- (multiple-value-bind
- (body local-decs)
- (lisp::parse-defmacro arglist whole body name 'def-type-translator
- :default-default ''*)
- `(progn
- (cold-load-init
- (setf (info type translator ',name)
- #'(lambda (,whole) ,@local-decs (block ,name ,body))))
- ',name))))
-
-
- ;;; Defvars for these come later, after we have enough stuff defined.
- (proclaim '(special *wild-type* *universal-type* *empty-type*))
-
-
- ;;; This condition is signalled whenever we make a UNKNOWN-TYPE so that
- ;;; compiler warnings can be emitted as appropriate.
- ;;;
- (eval-when (compile load eval)
- (define-condition parse-unknown-type (condition)
- (specifier)))
-
-
- ;;;; Cold load hack magic.
-
- (eval-when (compile eval)
-
- (defparameter cold-type-init-forms nil
- "Forms that must happen before top level forms are run.")
-
- (defparameter cold-type-init-defuns nil
- "Function names that enclose the above.")
-
- (defmacro cold-load-init (&rest forms)
- (if (and (consp forms) (consp (car forms)) (eq (caar forms) 'eval-when))
- (let ((when (cadar forms))
- (eval-when-forms (cddar forms)))
- (unless (= (length forms) 1)
- (warn "Can't cold-load-init other forms along with an eval-when."))
- (when (member 'load when)
- (setf cold-type-init-forms
- (nconc cold-type-init-forms (copy-list eval-when-forms))))
- `(eval-when ,(remove 'load when)
- ,@eval-when-forms))
- (progn
- (setf cold-type-init-forms
- (nconc cold-type-init-forms (copy-list forms)))
- nil)))
-
- (defmacro emit-cold-load-defuns ()
- (let ((index 0))
- (collect ((defuns))
- (loop
- (unless cold-type-init-forms (return))
- (let ((num-forms (min 10 (length cold-type-init-forms)))
- (name (intern (format nil "TYPE-INIT-~D" (incf index)))))
- (defuns `(defun ,name ()
- ,@(subseq cold-type-init-forms 0 num-forms)))
- (setf cold-type-init-forms (nthcdr num-forms cold-type-init-forms))
- (push (list name) cold-type-init-defuns)))
- (setf cold-type-init-defuns (nreverse cold-type-init-defuns))
- `(progn
- ,@(defuns)))))
-
- (defmacro do-cold-load-init-forms ()
- `(progn
- ,@cold-type-init-defuns))
-
- ); eval-when
-
- #+nil ;; Use this definition if you are trying to use this interactivly.
- (defmacro cold-load-init (&rest forms)
- `(progn ,@forms))
-
-
-
- ;;;; Type classes:
- ;;;
- ;;; The TYPE-CLASS structure represents the "kind" of a type. It mainly
- ;;; contains functions which are methods on that kind of type, but is also use
- ;;; in EQ comparisons to determined if two types have the "same kind".
-
- (defvar *type-classes*)
-
- ;;; TYPE-CLASS-OR-LOSE -- Internal
- ;;;
- (defun type-class-or-lose (name)
- (or (gethash name *type-classes*)
- (error "~S is not a defined type class." name)))
-
- ;;; MUST-SUPPLY-THIS -- Internal
- ;;;
- (defun must-supply-this (&rest foo)
- (error "Missing type method for ~S" foo))
-
-
- (defstruct (type-class
- (:print-function
- (lambda (s stream d)
- (declare (ignore d))
- (format stream "#<TYPE-CLASS ~S>" (type-class-name s)))))
-
- ;;
- ;; Name of this type class, used to resolve references at load time.
- (name nil :type symbol)
- ;;
- ;; Dyadic type methods. If the classes of the two types are EQ, then we call
- ;; the SIMPLE-xxx method. If the classes are not EQ, and either type's class
- ;; has a COMPLEX-xxx method, then we call it.
- ;;
- ;; Although it is undefined which method will get precedence when both types
- ;; have a complex method, the complex method can assume that the second arg
- ;; always is in its class, and the first always is not. The arguments to
- ;; commutative operations will be swapped if the first argument has a complex
- ;; method.
- ;;
- ;; Since SUBTYPEP is not commutative, we have two complex methods. the ARG1
- ;; method is only called when the first argument is in its class, and the
- ;; ARG2 method is only called when called when the second type is. If either
- ;; is specified, both must be.
- (simple-subtypep #'must-supply-this :type function)
- (complex-subtypep-arg1 nil :type (or function null))
- (complex-subtypep-arg2 nil :type (or function null))
- ;;
- ;; If SIMPLE-UNION is unspecified, then the union is computed to be the
- ;; supertype (if a subtype relationship exists), or a two type union.
- ;; SIMPLE-UNION should return NIL when the result would be the two type
- ;; union.
- (simple-union nil :type (or function null))
- (complex-union nil :type (or function null))
- ;;
- ;; The default intersection methods assume that there is an intersection iff
- ;; a subtype relationship exists.
- (simple-intersection #'vanilla-intersection :type function)
- (complex-intersection nil :type (or function null))
- ;;
- (simple-= #'must-supply-this :type function)
- (complex-= nil :type (or function null))
- ;;
- ;; Function which returns a Common Lisp type specifier representing this
- ;; type.
- (unparse #'must-supply-this :type function)
-
- #|
- Not used, and not really right. Probably we want a TYPE= alist for the
- unary operations, since there are lots of interesting unary predicates that
- aren't equivalent to an entire class
- ;;
- ;; Names of functions used for testing the type of objects in this type
- ;; class. UNARY-PREDICATE takes just the object, whereas PREDICATE gets
- ;; passed both the object and the CTYPE. Normally one or the other will be
- ;; supplied for any type that can be passed to TYPEP; there is no point in
- ;; supplying both.
- (unary-typep nil :type (or symbol null))
- (typep nil :type (or symbol null))
- ;;
- ;; Like TYPEP, UNARY-TYPEP except these functions coerce objects to this
- ;; type.
- (unary-coerce nil :type (or symbol null))
- (coerce :type (or symbol null))
- |#
- )
-
-
- (eval-when (compile load eval)
-
- (defconstant type-class-function-slots
- '((:simple-subtypep . type-class-simple-subtypep)
- (:complex-subtypep-arg1 . type-class-complex-subtypep-arg1)
- (:complex-subtypep-arg2 . type-class-complex-subtypep-arg2)
- (:simple-union . type-class-simple-union)
- (:complex-union . type-class-complex-union)
- (:simple-intersection . type-class-simple-intersection)
- (:complex-intersection . type-class-complex-intersection)
- (:simple-= . type-class-simple-=)
- (:complex-= . type-class-complex-=)
- (:unparse . type-class-unparse)))
-
- (defconstant type-class-symbol-slots
- '((:unary-typep . type-class-unary-typep)
- (:typep . type-class-typep)
- (:unary-coerce . type-class-unary-coerce)
- (:coerce . type-class-coerce)))
-
-
- ;;; CLASS-FUNCTION-SLOT-OR-LOSE -- Internal
- ;;;
- (defun class-function-slot-or-lose (name)
- (or (cdr (assoc name type-class-function-slots))
- (error "~S is not a defined type class method." name)))
-
- ); Eval-When (Compile Load Eval)
-
-
- ;;; DEFINE-TYPE-METHOD -- Internal
- ;;;
- (defmacro define-type-method ((class method &rest more-methods)
- lambda-list &body body)
- "DEFINE-TYPE-METHOD (Class-Name Method-Name+) Lambda-List Form*"
- (let ((name (symbolicate CLASS "-" method "-TYPE-METHOD")))
- `(progn
- (defun ,name ,lambda-list ,@body)
- (cold-load-init
- ,@(mapcar #'(lambda (method)
- `(setf (,(class-function-slot-or-lose method)
- (type-class-or-lose ',class))
- #',name))
- (cons method more-methods)))
- (undefined-value))))
-
-
- ;;; DEFINE-TYPE-CLASS -- Internal
- ;;;
- (defmacro define-type-class (name &optional inherits)
- "DEFINE-TYPE-CLASS Name [Inherits]"
- `(cold-load-init
- ,(once-only ((n-class (if inherits
- `(copy-type-class (type-class-or-lose ',inherits))
- '(make-type-class))))
- `(progn
- (setf (type-class-name ,n-class) ',name)
- (setf (gethash ',name *type-classes*) ,n-class)
- (undefined-value)))))
-
-
- ;;; INVOKE-TYPE-METHOD -- Internal
- ;;;
- ;;; Invoke a type method on TYPE1 and TYPE2. If the two types have the same
- ;;; class, invoke the simple method. Otherwise, invoke any complex method. If
- ;;; there isn't a distinct complex-arg1 method, then swap the arguments when
- ;;; calling type1's method. If no applicable method, return DEFAULT.
- ;;;
- (defmacro invoke-type-method (simple complex-arg2 type1 type2 &key
- (default '(values nil t))
- complex-arg1)
- (let ((simple (class-function-slot-or-lose simple))
- (cslot1 (class-function-slot-or-lose (or complex-arg1 complex-arg2)))
- (cslot2 (class-function-slot-or-lose complex-arg2)))
- (once-only ((n-type1 type1)
- (n-type2 type2))
- (once-only ((class1 `(type-class-info ,n-type1))
- (class2 `(type-class-info ,n-type2)))
- `(if (eq ,class1 ,class2)
- (funcall (,simple ,class1) ,n-type1 ,n-type2)
- ,(once-only ((complex1 `(,cslot1 ,class1))
- (complex2 `(,cslot2 ,class2)))
- `(cond (,complex2 (funcall ,complex2 ,n-type1 ,n-type2))
- (,complex1
- ,(if complex-arg1
- `(funcall ,complex1 ,n-type1 ,n-type2)
- `(funcall ,complex1 ,n-type2 ,n-type1)))
- (t ,default))))))))
-
-
- ;;; The XXX-Type structures include the CTYPE structure for some slots that
- ;;; apply to all types.
- ;;;
- (defstruct (ctype (:conc-name type-))
- ;;
- ;; The class of this type.
- (class-info (required-argument) :type type-class)
- ;;
- ;; True if this type has a fixed number of members, and as such could
- ;; possibly be completely specified in a MEMBER type. This is used by the
- ;; MEMBER type methods.
- (enumerable nil :type (member t nil) :read-only t))
-
- ;;; %Print-Type -- Internal
- ;;;
- ;;; The print-function for all type structures.
- ;;;
- (defun %print-type (s stream d)
- (declare (ignore d))
- (format stream "#<~A ~S>" (type-of s) (type-specifier s)))
-
-
- ;;;; Utilities:
-
- ;;; ANY-TYPE-OP, EVERY-TYPE-OP -- Internal
- ;;;
- ;;; Like ANY and EVERY, except that we handle two-arg uncertain predicates.
- ;;; If the result is uncertain, then we return Default from the block PUNT.
- ;;; If LIST-FIRST is true, then the list element is the first arg, otherwise
- ;;; the second.
- ;;;
- (defmacro any-type-op (op thing list &key (default '(values nil nil))
- list-first)
- (let ((n-this (gensym))
- (n-thing (gensym))
- (n-val (gensym))
- (n-win (gensym))
- (n-uncertain (gensym)))
- `(let ((,n-thing ,thing)
- (,n-uncertain nil))
- (dolist (,n-this ,list
- (if ,n-uncertain
- (return-from PUNT ,default)
- nil))
- (multiple-value-bind (,n-val ,n-win)
- ,(if list-first
- `(,op ,n-this ,n-thing)
- `(,op ,n-thing ,n-this))
- (unless ,n-win (setq ,n-uncertain t))
- (when ,n-val (return t)))))))
- ;;;
- (defmacro every-type-op (op thing list &key (default '(values nil nil))
- list-first)
- (let ((n-this (gensym))
- (n-thing (gensym))
- (n-val (gensym))
- (n-win (gensym)))
- `(let ((,n-thing ,thing))
- (dolist (,n-this ,list t)
- (multiple-value-bind (,n-val ,n-win)
- ,(if list-first
- `(,op ,n-this ,n-thing)
- `(,op ,n-thing ,n-this))
- (unless ,n-win (return-from PUNT ,default))
- (unless ,n-val (return nil)))))))
-
-
-
- ;;; VANILLA-INTERSECTION -- Internal
- ;;;
- ;;; Compute the intersection for types that intersect only when one is a
- ;;; subtype of the other.
- ;;;
- (defun vanilla-intersection (type1 type2)
- (multiple-value-bind (stp1 win1)
- (csubtypep type1 type2)
- (multiple-value-bind (stp2 win2)
- (csubtypep type2 type1)
- (cond (stp1 (values type1 t))
- (stp2 (values type2 t))
- ((and win1 win2) (values *empty-type* t))
- (t
- (values type1 nil))))))
-
-
- ;;; Def-Builtin-Type -- Internal
- ;;;
- ;;; Take a name and a type and define it as a builtin type.
- ;;;
- (defmacro def-builtin-type (symbol type)
- `(cold-load-init
- (%def-builtin-type ,symbol ,type)))
- ;;;
- (proclaim '(function %def-builtin-type (symbol ctype) void))
- (defun %def-builtin-type (name type)
- (check-type name symbol)
- (check-type type ctype)
- (setf (info type builtin name) type))
-
-
- ;;; Precompute-Types -- Internal
- ;;;
- ;;; Take a list of type specifiers, compute the translation and define it as
- ;;; a builtin type.
- ;;;
- (proclaim '(function precompute-types (list) void))
- (defun precompute-types (specs)
- (dolist (spec specs)
- (let ((res (specifier-type spec)))
- (unless (unknown-type-p res)
- (setf (info type builtin spec) res)))))
-
-
- ;;; TYPE-CACHE-HASH -- Internal
- ;;;
- ;;; EQ hash two things (types) down to 8 bits.
- ;;;
- (defmacro type-cache-hash (type1 type2)
- `(the fixnum
- (logand (the fixnum
- (logxor (the fixnum
- (ash (cache-hash-eq ,type1) -3))
- (the fixnum (cache-hash-eq ,type2))))
- #xFF)))
-
-
- ;;;; Function and Values types.
- ;;;
- ;;; Pretty much all of the general type operations are illegal on VALUES
- ;;; types, since we can't discriminate using them, do SUBTYPEP, etc. FUNCTION
- ;;; types are acceptable to the normal type operations, but are generally
- ;;; considered to be equivalent to FUNCTION. These really aren't true types in
- ;;; any type theoretic sense, but we still parse them into CTYPE structures for
- ;;; two reasons:
- ;;; -- Parsing and unparsing work the same way, and indeed we can't tell
- ;;; whether a type is a function or values type without parsing it.
- ;;; -- Many of the places that can be annotated with real types can also be
- ;;; annotated function or values types.
-
-
- ;;; The Args-Type structure is used both to represent Values types and
- ;;; and Function types.
- ;;;
- (defstruct (args-type (:include ctype)
- (:print-function %print-type))
- ;;
- ;; Lists of the type for each required and optional argument.
- (required nil :type list)
- (optional nil :type list)
- ;;
- ;; The type for the rest arg. NIL if there is no rest arg.
- (rest nil :type (or ctype null))
- ;;
- ;; True if keyword arguments are specified.
- (keyp nil :type boolean)
- ;;
- ;; List of key-info structures describing the keyword arguments.
- (keywords nil :type list)
- ;;
- ;; True if other keywords are allowed.
- (allowp nil :type boolean))
-
- (defstruct key-info
- ;;
- ;; The keyword.
- (name (required-argument) :type keyword)
- ;;
- ;; Type of this argument.
- (type (required-argument) :type ctype))
-
-
- (define-type-class values)
-
- (define-type-method (values :simple-subtypep :complex-subtypep-arg1)
- (type1 type2)
- (declare (ignore type2))
- (error "Subtypep is illegal on this type:~% ~S" (type-specifier type1)))
-
- (define-type-method (values :complex-subtypep-arg2)
- (type1 type2)
- (declare (ignore type1))
- (error "Subtypep is illegal on this type:~% ~S" (type-specifier type2)))
-
- (defstruct (values-type
- (:include args-type
- (:class-info (type-class-or-lose 'values)))
- (:print-function %print-type)))
-
- (define-type-method (values :unparse) (type)
- (cons 'values (unparse-args-types type)))
-
-
- ;;; TYPE=-LIST -- Internal
- ;;;
- ;;; Return true if List1 and List2 have the same elements in the same
- ;;; positions according to TYPE=. We return NIL, NIL if there is an uncertain
- ;;; comparison.
- ;;;
- (defun type=-list (list1 list2)
- (declare (list list1 list2))
- (do ((types1 list1 (cdr types1))
- (types2 list2 (cdr types2)))
- ((or (null types1) (null types2))
- (if (or types1 types2)
- (values nil t)
- (values t t)))
- (multiple-value-bind (val win)
- (type= (first types1) (first types2))
- (unless win
- (return (values nil nil)))
- (unless val
- (return (values nil t))))))
-
-
- (define-type-method (values :simple-=) (type1 type2)
- (let ((rest1 (args-type-rest type1))
- (rest2 (args-type-rest type2)))
- (cond ((or (args-type-keyp type1) (args-type-keyp type2)
- (args-type-allowp type1) (args-type-allowp type2))
- (values nil nil))
- ((and rest1 rest2 (type/= rest1 rest2))
- (type= rest1 rest2))
- ((or rest1 rest2)
- (values nil t))
- (t
- (multiple-value-bind (req-val req-win)
- (type=-list (values-type-required type1)
- (values-type-required type2))
- (multiple-value-bind (opt-val opt-win)
- (type=-list (values-type-optional type1)
- (values-type-optional type2))
- (values (and req-val opt-val) (and req-win opt-win))))))))
-
-
- (define-type-class function)
-
- (defstruct (function-type
- (:include args-type
- (class-info (type-class-or-lose 'function)))
- (:print-function %print-type))
- ;;
- ;; True if the arguments are unrestrictive, i.e. *.
- (wild-args nil :type boolean)
- ;;
- ;; Type describing the return values. This is a values type
- ;; when multiple values were specified for the return.
- (returns (required-argument) :type ctype))
-
-
- ;;; A flag that we can bind to cause complex function types to be unparsed as
- ;;; FUNCTION. Useful when we want a type that we can pass to TYPEP.
- ;;;
- (defvar *unparse-function-type-simplify*)
-
- (define-type-method (function :unparse) (type)
- (if *unparse-function-type-simplify*
- 'function
- (list 'function
- (if (function-type-wild-args type)
- '*
- (unparse-args-types type))
- (type-specifier
- (function-type-returns type)))))
-
-
- ;;; Since all function types are equivalent to FUNCTION, they are all subtypes
- ;;; of each other.
- ;;;
- (define-type-method (function :simple-subtypep) (type1 type2)
- (declare (ignore type1 type2))
- (values t t))
-
-
- ;;; The union or intersection of two FUNCTION types is FUNCTION.
- ;;;
- (define-type-method (function :simple-union) (type1 type2)
- (declare (ignore type1 type2))
- (specifier-type 'function))
- ;;;
- (define-type-method (function :simple-intersection) (type1 type2)
- (declare (ignore type1 type2))
- (values (specifier-type 'function) t))
-
-
- ;;; ### Not very real, but good enough for redefining transforms according to
- ;;; type:
- ;;;
- (define-type-method (function :simple-=) (type1 type2)
- (values (equalp type1 type2) t))
-
-
- (define-type-class constant values)
-
- ;;; The CONSTANT-TYPE structure represents a use of the CONSTANT-ARGUMENT "type
- ;;; specifier", which is only meaningful in function argument type specifiers
- ;;; used within the compiler.
- ;;;
- (defstruct (constant-type (:include ctype
- (class-info (type-class-or-lose 'constant)))
- (:print-function %print-type))
- ;;
- ;; The type which the argument must be a constant instance of for this type
- ;; specifier to win.
- (type (required-argument) :type ctype))
-
- (define-type-method (constant :unparse) (type)
- `(constant-argument ,(type-specifier (constant-type-type type))))
-
- (define-type-method (constant :simple-=) (type1 type2)
- (type= (constant-type-type type1) (constant-type-type type2)))
-
- (def-type-translator constant-argument (type)
- (make-constant-type :type (specifier-type type)))
-
-
- ;;; Parse-Args-Types -- Internal
- ;;;
- ;;; Given a lambda-list like values type specification and a Args-Type
- ;;; structure, fill in the slots in the structure accordingly. This is used
- ;;; for both FUNCTION and VALUES types.
- ;;;
- (proclaim '(function parse-args-types (list args-type) void))
- (defun parse-args-types (lambda-list result)
- (multiple-value-bind (required optional restp rest keyp keys allowp aux)
- (parse-lambda-list lambda-list)
- (when aux
- (error "&Aux in a FUNCTION or VALUES type: ~S." lambda-list))
- (setf (args-type-required result) (mapcar #'specifier-type required))
- (setf (args-type-optional result) (mapcar #'specifier-type optional))
- (setf (args-type-rest result) (if restp (specifier-type rest) nil))
- (setf (args-type-keyp result) keyp)
- (collect ((key-info))
- (dolist (key keys)
- (when (or (atom key) (/= (length key) 2))
- (error "Keyword type description is not a two-list: ~S." key))
- (let* ((name (first key))
- (kwd (if (keywordp name) name
- (intern (symbol-name name) "KEYWORD"))))
- (when (find kwd (key-info) :key #'key-info-name)
- (error "Repeated keyword ~S in lambda list: ~S." kwd lambda-list))
- (key-info (make-key-info :name kwd
- :type (specifier-type (second key))))))
- (setf (args-type-keywords result) (key-info)))
- (setf (args-type-allowp result) allowp)))
-
-
- ;;; Unparse-Args-Types -- Internal
- ;;;
- ;;; Return the lambda-list like type specification corresponding
- ;;; to a Args-Type.
- ;;;
- (proclaim '(function unparse-args-types (args-type) list))
- (defun unparse-args-types (type)
- (collect ((result))
-
- (dolist (arg (args-type-required type))
- (result (type-specifier arg)))
-
- (when (args-type-optional type)
- (result '&optional)
- (dolist (arg (args-type-optional type))
- (result (type-specifier arg))))
-
- (when (args-type-rest type)
- (result '&rest)
- (result (type-specifier (args-type-rest type))))
-
- (when (args-type-keyp type)
- (result '&key)
- (dolist (key (args-type-keywords type))
- (result (list (key-info-name key)
- (type-specifier (key-info-type key))))))
-
- (when (args-type-allowp type)
- (result '&allow-other-keys))
-
- (result)))
-
-
- (def-type-translator function (&optional args result)
- (let ((res (make-function-type
- :returns (values-specifier-type result))))
- (if (eq args '*)
- (setf (function-type-wild-args res) t)
- (parse-args-types args res))
- res))
-
-
- (def-type-translator values (&rest values)
- (let ((res (make-values-type)))
- (parse-args-types values res)
- res))
-
-
- ;;;; Values types interfaces:
- ;;;
- ;;; We provide a few special operations that can be meaningfully used on
- ;;; values types (as well as on any other type.)
- ;;;
-
- ;;; Single-Value-Type -- Interface
- ;;;
- ;;; Return the type of the first value indicated by Type. This is used by
- ;;; people who don't want to have to deal with values types.
- ;;;
- (defun single-value-type (type)
- (declare (type ctype type))
- (cond ((values-type-p type)
- (cond ((args-type-required type)
- (first (args-type-required type)))
- ((args-type-optional type)
- (first (args-type-optional type)))
- ((args-type-rest type))
- (t
- *universal-type*)))
- ((eq type *wild-type*)
- *universal-type*)
- (t
- type)))
-
-
- ;;; FUNCTION-TYPE-NARGS -- Interface
- ;;;
- ;;; Return the minmum number of arguments that a function can be called
- ;;; with, and the maximum number or NIL. If not a function type, return
- ;;; NIL, NIL.
- ;;;
- (defun function-type-nargs (type)
- (declare (type ctype type))
- (if (function-type-p type)
- (let ((fixed (length (args-type-required type))))
- (if (or (args-type-rest type)
- (args-type-keyp type)
- (args-type-allowp type))
- (values fixed nil)
- (values fixed (+ fixed (length (args-type-optional type))))))
- (values nil nil)))
-
-
- ;;; Values-Types -- Interface
- ;;;
- ;;; Determine if Type corresponds to a definite number of values. The first
- ;;; value is a list of the types for each value, and the second value is the
- ;;; number of values. If the number of values is not fixed, then return NIL
- ;;; and :Unknown.
- ;;;
- (defun values-types (type)
- (declare (type ctype type))
- (cond ((eq type *wild-type*)
- (values nil :unknown))
- ((not (values-type-p type))
- (values (list type) 1))
- ((or (args-type-optional type)
- (args-type-rest type)
- (args-type-keyp type)
- (args-type-allowp type))
- (values nil :unknown))
- (t
- (let ((req (args-type-required type)))
- (values (mapcar #'single-value-type req) (length req))))))
-
-
- ;;; Values-Type-Types -- Internal
- ;;;
- ;;; Return two values:
- ;;; 1] A list of all the positional (fixed and optional) types.
- ;;; 2] The rest type (if any). If keywords allowed, *universal-type*. If no
- ;;; keywords or rest, *empty-type*.
- ;;;
- (defun values-type-types (type)
- (declare (type values-type type))
- (values (append (args-type-required type)
- (args-type-optional type))
- (cond ((args-type-keyp type) *universal-type*)
- ((args-type-rest type))
- (t
- *empty-type*))))
-
-
- ;;; Fixed-Values-Op -- Internal
- ;;;
- ;;; Return a list of Operation applied to the types in Types1 and Types2,
- ;;; padding with Rest2 as needed. Types1 must not be shorter than Types2. The
- ;;; second value is T if Operation always returned a true second value.
- ;;;
- (defun fixed-values-op (types1 types2 rest2 operation)
- (declare (list types1 types2) (type ctype rest2) (type function operation))
- (let ((exact t))
- (values (mapcar #'(lambda (t1 t2)
- (multiple-value-bind (res win)
- (funcall operation t1 t2)
- (unless win (setq exact nil))
- res))
- types1
- (append types2
- (make-list (- (length types1) (length types2))
- :initial-element rest2)))
- exact)))
-
-
- ;;; Coerce-To-Values -- Internal
- ;;;
- ;;; If Type isn't a values type, then make it into one:
- ;;; <type> ==> (values type &rest t)
- ;;;
- (defun coerce-to-values (type)
- (declare (type ctype type))
- (if (values-type-p type)
- type
- (make-values-type :required (list type) :rest *universal-type*)))
-
-
- ;;; Args-Type-Op -- Internal
- ;;;
- ;;; Do the specified Operation on Type1 and Type2, which may be any type,
- ;;; including Values types. With values types such as:
- ;;; (values a0 a1)
- ;;; (values b0 b1)
- ;;;
- ;;; We compute the more useful result:
- ;;; (values (<operation> a0 b0) (<operation> a1 b1))
- ;;;
- ;;; Rather than the precise result:
- ;;; (<operation> (values a0 a1) (values b0 b1))
- ;;;
- ;;; This has the virtue of always keeping the values type specifier outermost,
- ;;; and retains all of the information that is really useful for static type
- ;;; analysis. We want to know what is always true of each value independently.
- ;;; It is worthless to know that IF the first value is B0 then the second will
- ;;; be B1.
- ;;;
- ;;; If the values count signatures differ, then we produce result with the
- ;;; required value count chosen by Nreq when applied to the number of required
- ;;; values in type1 and type2. Any &key values become &rest T (anyone who uses
- ;;; keyword values deserves to lose.)
- ;;;
- ;;; The second value is true if the result is definitely empty or if Operation
- ;;; returned true as its second value each time we called it. Since we
- ;;; approximate the intersection of values types, the second value being true
- ;;; doesn't mean the result is exact.
- ;;;
- (defun args-type-op (type1 type2 operation nreq)
- (declare (type ctype type1 type2) (type function operation nreq))
- (if (or (values-type-p type1) (values-type-p type2))
- (let ((type1 (coerce-to-values type1))
- (type2 (coerce-to-values type2)))
- (multiple-value-bind (types1 rest1)
- (values-type-types type1)
- (multiple-value-bind (types2 rest2)
- (values-type-types type2)
- (multiple-value-bind (rest rest-exact)
- (funcall operation rest1 rest2)
- (multiple-value-bind
- (res res-exact)
- (if (< (length types1) (length types2))
- (fixed-values-op types2 types1 rest1 operation)
- (fixed-values-op types1 types2 rest2 operation))
- (let* ((req (funcall nreq
- (length (args-type-required type1))
- (length (args-type-required type2))))
- (required (subseq res 0 req))
- (opt (subseq res req))
- (opt-last (position rest opt :test-not #'type=
- :from-end t)))
- (if (find *empty-type* required :test #'type=)
- (values *empty-type* t)
- (values (make-values-type
- :required required
- :optional (if opt-last
- (subseq opt 0 (1+ opt-last))
- ())
- :rest (if (eq rest *empty-type*) nil rest))
- (and rest-exact res-exact)))))))))
- (funcall operation type1 type2)))
-
-
- ;;; Values-Type-Union, Values-Type-Intersection -- Interface
- ;;;
- ;;; Do a union or intersection operation on types that might be values
- ;;; types. The result is optimized for utility rather than exactness, but it
- ;;; is guaranteed that it will be no smaller (more restrictive) than the
- ;;; precise result.
- ;;;
- (defun-cached (values-type-union :hash-function type-cache-hash
- :hash-bits 8
- :default nil)
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (cond ((or (eq type1 *wild-type*) (eq type2 *wild-type*)) *wild-type*)
- ((eq type1 *empty-type*) type2)
- ((eq type2 *empty-type*) type1)
- (t
- (values (args-type-op type1 type2 #'type-union #'min)))))
- ;;;
- (defun-cached (values-type-intersection :hash-function type-cache-hash
- :hash-bits 8
- :values 2
- :default (values nil :empty))
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (cond ((eq type1 *wild-type*) (values type2 t))
- ((eq type2 *wild-type*) (values type1 t))
- (t
- (args-type-op type1 type2 #'type-intersection #'max))))
-
-
- ;;; Values-Types-Intersect -- Interface
- ;;;
- ;;; Like Types-Intersect, except that it sort of works on values types.
- ;;; Note that due to the semantics of Values-Type-Intersection, this might
- ;;; return {T, T} when there isn't really any intersection (?).
- ;;;
- (defun values-types-intersect (type1 type2)
- (cond ((or (eq type1 *empty-type*) (eq type2 *empty-type*))
- (values t t))
- ((or (values-type-p type1) (values-type-p type2))
- (multiple-value-bind (res win)
- (values-type-intersection type1 type2)
- (values (not (eq res *empty-type*))
- win)))
- (t
- (types-intersect type1 type2))))
-
-
- ;;; Values-Subtypep -- Interface
- ;;;
- ;;; A subtypep-like operation that can be used on any types, including
- ;;; values types.
- ;;;
- (defun-cached (values-subtypep :hash-function type-cache-hash
- :hash-bits 8
- :values 2
- :default (values nil :empty))
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (cond ((eq type2 *wild-type*) (values t t))
- ((eq type1 *wild-type*)
- (values (eq type2 *universal-type*) t))
- ((not (values-types-intersect type1 type2))
- (values nil t))
- (t
- (if (or (values-type-p type1) (values-type-p type2))
- (let ((type1 (coerce-to-values type1))
- (type2 (coerce-to-values type2)))
- (multiple-value-bind (types1 rest1)
- (values-type-types type1)
- (multiple-value-bind (types2 rest2)
- (values-type-types type2)
- (cond ((< (length (values-type-required type1))
- (length (values-type-required type2)))
- (values nil t))
- ((< (length types1) (length types2))
- (values nil nil))
- ((or (values-type-keyp type1)
- (values-type-keyp type2))
- (values nil nil))
- (t
- (do ((t1 types1 (rest t1))
- (t2 types2 (rest t2)))
- ((null t2)
- (csubtypep rest1 rest2))
- (multiple-value-bind
- (res win-p)
- (csubtypep (first t1) (first t2))
- (unless win-p
- (return (values nil nil)))
- (unless res
- (return (values nil t))))))))))
- (csubtypep type1 type2)))))
-
-
- ;;;; Type method interfaces:
-
- ;;; Csubtypep -- Interface
- ;;;
- ;;; Like subtypep, only works on Type structures.
- ;;;
- (defun-cached (csubtypep :hash-function type-cache-hash
- :hash-bits 8
- :values 2
- :default (values nil :empty))
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (cond ((or (eq type1 type2)
- (eq type1 *empty-type*)
- (eq type2 *wild-type*))
- (values t t))
- ((or (eq type1 *wild-type*)
- (eq type2 *empty-type*))
- (values nil t))
- (t
- (invoke-type-method :simple-subtypep :complex-subtypep-arg2
- type1 type2
- :complex-arg1 :complex-subtypep-arg1))))
-
-
- ;;; Type= -- Interface
- ;;;
- ;;; If two types are definitely equivalent, return true. The second value
- ;;; indicates whether the first value is definitely correct. This should only
- ;;; fail in the presence of Hairy types.
- ;;;
- (defun-cached (type= :hash-function type-cache-hash
- :hash-bits 8
- :values 2
- :default (values nil :empty))
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (if (eq type1 type2)
- (values t t)
- (invoke-type-method :simple-= :complex-= type1 type2)))
-
-
- ;;; TYPE/= -- Interface
- ;;;
- ;;; Not exactly the negation of TYPE=, since when the relationship is
- ;;; uncertain, we still return NIL, NIL. This is useful in cases where the
- ;;; conservative assumption is =.
- ;;;
- (defun type/= (type1 type2)
- (declare (type ctype type1 type2))
- (multiple-value-bind (res win)
- (type= type1 type2)
- (if win
- (values (not res) t)
- (values nil nil))))
-
-
- ;;; Type-Union -- Interface
- ;;;
- ;;; Find a type which includes both types. Any inexactness is represented
- ;;; by the fuzzy element types; we return a single value that is precise to the
- ;;; best of our knowledge. This result is simplified into the canonical form,
- ;;; thus is not a UNION type unless there is no other way to represent the
- ;;; result.
- ;;;
- ;;; We can't use INVOKE-TYPE-METHOD because the :SIMPLE-UNION method may be
- ;;; missing (meaning use subtype relations.)
- ;;;
- (defun-cached (type-union :hash-function type-cache-hash
- :hash-bits 8)
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (if (eq type1 type2)
- type1
- (let ((class1 (type-class-info type1))
- (class2 (type-class-info type2)))
- (if (eq class1 class2)
- (let ((method (type-class-simple-union class1)))
- (cond (method
- (let ((union (funcall method type1 type2)))
- (or union
- (make-union-type (list type1 type2)))))
- ((csubtypep type1 type2) type2)
- ((csubtypep type2 type1) type1)
- (t
- (make-union-type (list type1 type2)))))
- (let ((complex1 (type-class-complex-union class1))
- (complex2 (type-class-complex-union class2)))
- (cond (complex2 (funcall complex2 type1 type2))
- (complex1 (funcall complex1 type2 type1))
- (t
- (make-union-type (list type1 type2)))))))))
-
-
- ;;; Type-Intersection -- Interface
- ;;;
- ;;; Return as restrictive a type as we can discover that is no more
- ;;; restrictive than the intersection of Type1 and Type2. The second value is
- ;;; true if the result is exact. At worst, we randomly return one of the
- ;;; arguments as the first value (trying not to return a hairy type).
- ;;;
- (defun-cached (type-intersection :hash-function type-cache-hash
- :hash-bits 8
- :values 2
- :default (values nil :empty))
- ((type1 eq) (type2 eq))
- (declare (type ctype type1 type2))
- (if (eq type1 type2)
- (values type1 t)
- (invoke-type-method :simple-intersection :complex-intersection
- type1 type2
- :default (values *empty-type* t))))
-
-
- ;;; Types-Intersect -- Interface
- ;;;
- ;;; The first value is true unless the types don't intersect. The second
- ;;; value is true if the first value is definitely correct. NIL is considered
- ;;; to intersect with any type. If T is a subtype of either type, then we also
- ;;; return T, T. This way we consider hairy types to intersect with T.
- ;;;
- (defun types-intersect (type1 type2)
- (declare (type ctype type1 type2))
- (if (or (eq type1 *empty-type*) (eq type2 *empty-type*))
- (values t t)
- (multiple-value-bind (val winp)
- (type-intersection type1 type2)
- (cond ((not winp)
- (if (or (csubtypep *universal-type* type1)
- (csubtypep *universal-type* type2))
- (values t t)
- (values t nil)))
- ((eq val *empty-type*) (values nil t))
- (t (values t t))))))
-
-
- ;;; Type-Specifier -- Interface
- ;;;
- ;;; Return a Common Lisp type specifier corresponding to this type.
- ;;;
- (defun type-specifier (type)
- (declare (type ctype type))
- (funcall (type-class-unparse (type-class-info type)) type))
-
-
- ;;; VALUES-SPECIFIER-TYPE -- Interface
- ;;;
- ;;; Return the type structure corresponding to a type specifier. We pick
- ;;; off Structure types as a special case.
- ;;;
- ;;; Note: SPECIFIER-TYPE-CACHE-CLEAR must be called whenever a type is defined
- ;;; (or redefined).
- ;;;
- (defun-cached (values-specifier-type
- :hash-function (lambda (x)
- (the fixnum
- (logand (the fixnum (cache-hash-eq x))
- #x3FF)))
- :hash-bits 10)
- ((spec eq))
- (or (info type builtin spec)
- (let ((expand (type-expand spec)))
- (if (eq expand spec)
- (let* ((lspec (if (atom spec) (list spec) spec))
- (fun (info type translator (car lspec))))
- (cond
- (fun (funcall fun lspec))
- ((and (symbolp spec)
- (eq (info type kind spec) :structure))
- (make-structure-type :name spec))
- ((or (and (consp spec) (symbolp (car spec)))
- (symbolp spec))
- (signal 'parse-unknown-type :specifier spec)
- ;;
- ;; Inhibit caching...
- (return-from values-specifier-type
- (make-unknown-type :specifier spec)))
- (t
- (error "Bad thing to be a type specifier: ~S."
- spec))))
- (values-specifier-type expand)))))
-
-
- ;;; SPECIFIER-TYPE -- Interface
- ;;;
- ;;; Like VALUES-SPECIFIER-TYPE, except that we guarantee to never return a
- ;;; VALUES type.
- ;;;
- (defun specifier-type (x)
- (let ((res (values-specifier-type x)))
- (when (values-type-p res)
- (error "VALUES type illegal in this context:~% ~S" x))
- res))
-
-
- ;;; Type-Expand -- Interface
- ;;;
- ;;; Similar to Macroexpand, but expands deftypes. We don't bother returning
- ;;; a second value.
- ;;;
- (defun type-expand (form)
- (let ((def (cond ((symbolp form)
- (info type expander form))
- ((and (consp form) (symbolp (car form)))
- (info type expander (car form)))
- (t nil))))
- (if def
- (type-expand (funcall def (if (consp form) form (list form))))
- form)))
-
-
-
- ;;;; Builtin types.
-
- ;;; The Named-Type is used to represent types which have no additional
- ;;; information and don't have a more specific representation.
- (defstruct (named-type (:include ctype
- (class-info (type-class-or-lose 'named)))
- (:print-function %print-type))
- ;;
- ;; The symbol name for this type.
- (name (required-argument) :type symbol)
- ;;
- ;; The names of (named) supertypes of this type. Includes Name.
- (supertypes nil :type list)
- ;;
- ;; The names the type classes which are subtypes of this type.
- (subclasses nil :type list))
-
- ;;; The Named class handles types whose only supertypes are other named types.
- ;;;
- (define-type-class named)
-
- (define-type-method (named :unparse) (type)
- (named-type-name type))
-
- ;;; We should never be called when the two types are equal, since the EQ check
- ;;; in TYPE= should detect that.
- ;;;
- (define-type-method (named :simple-=) (type1 type2)
- (assert (not (eq (named-type-name type1) (named-type-name type2))))
- (values nil t))
-
- (define-type-method (named :simple-subtypep) (type1 type2)
- (if (member (named-type-name type2)
- (named-type-supertypes type1))
- (values t t)
- (values nil t)))
-
- (define-type-method (named :complex-subtypep-arg1) (type1 type2)
- (let ((meth (type-class-complex-subtypep-arg2 (type-class-info type2))))
- (cond ((eq type1 *empty-type*) (values t t))
- (meth (funcall meth type1 type2))
- (t
- (values nil t)))))
-
- (define-type-method (named :complex-subtypep-arg2) (type1 type2)
- (let ((meth (type-class-complex-subtypep-arg1 (type-class-info type1))))
- (cond ((member (type-class-name (type-class-info type1))
- (named-type-subclasses type2))
- (values t t))
- (meth (funcall meth type1 type2))
- (t
- (values nil t)))))
-
- (define-type-method (named :complex-intersection) (type1 type2)
- (let ((meth (type-class-complex-intersection (type-class-info type1))))
- (if meth
- (funcall meth type2 type1)
- (vanilla-intersection type1 type2))))
-
- (define-type-method (named :complex-union) (type1 type2)
- (let* ((class1 (type-class-info type1))
- (union (type-class-complex-union class1)))
- (cond ((eq type2 *empty-type*) type1)
- ((csubtypep type1 type2) type2)
- (union (funcall union type2 type1))
- (t
- (make-union-type (list type1 type2))))))
-
- (def-builtin-type '*
- (make-named-type :name '*
- :supertypes '(*)
- :subclasses '(hairy number array member function
- structure alien)))
- ;;;
- (cold-load-init
- (defparameter *wild-type* (specifier-type '*)))
-
-
- (def-builtin-type 't
- (make-named-type :name 't
- :supertypes '(t *)
- :subclasses '(number array member function
- structure alien)))
- ;;;
- (cold-load-init
- (defparameter *universal-type* (specifier-type 't)))
-
-
- ;;; SUBTYPEP-ARG1 special-cases NIL to make this work.
- ;;;
- (def-builtin-type 'nil
- (make-named-type :name 'nil
- :supertypes '(* t character base-char standard-char
- extended-char function cons symbol keyword
- system-area-pointer weak-pointer
- scavenger-hook structure code-component
- lra fdefn nil)))
- ;;;
- (cold-load-init
- (defparameter *empty-type* (specifier-type 'nil)))
-
-
- (def-builtin-type 'character
- (make-named-type :name 'character
- :supertypes '(character t)
- :enumerable t))
-
- (def-builtin-type 'base-char
- (make-named-type :name 'base-char
- :supertypes '(base-char character t)
- :enumerable t))
-
- (def-builtin-type 'extended-char
- (make-named-type :name 'extended-char
- :supertypes '(extended-char character t)
- :enumerable t))
-
- (def-builtin-type 'standard-char
- (make-named-type :name 'standard-char
- :supertypes '(standard-char base-char character t)
- :enumerable t))
-
- (def-builtin-type 'function
- (make-named-type :name 'function
- :supertypes '(function t)
- :subclasses '(function)))
-
- (deftype compiled-function () 'function)
-
- (def-builtin-type 'cons
- (make-named-type :name 'cons
- :supertypes '(cons t)))
-
- (def-builtin-type 'symbol
- (make-named-type :name 'symbol
- :supertypes '(symbol t)))
-
- (def-builtin-type 'keyword
- (make-named-type :name 'keyword
- :supertypes '(keyword symbol t)))
-
- (def-builtin-type 'system-area-pointer
- (make-named-type :name 'system-area-pointer
- :supertypes '(system-area-pointer t)))
-
- (def-builtin-type 'weak-pointer
- (make-named-type :name 'weak-pointer
- :supertypes '(weak-pointer t)))
-
- (def-builtin-type 'scavenger-hook
- (make-named-type :name 'scavenger-hook
- :supertypes '(scavenger-hook t)))
-
- (def-builtin-type 'code-component
- (make-named-type :name 'code-component
- :supertypes '(code-component t)))
-
- (def-builtin-type 'lra
- (make-named-type :name 'lra
- :supertypes '(lra t)))
-
- (def-builtin-type 'fdefn
- (make-named-type :name 'fdefn
- :supertypes '(fdefn t)))
-
- ;;; STRUCTURE is a named type instead of a structure type, since it isn't
- ;;; really a sturcture.
- ;;;
- (def-builtin-type 'structure
- (make-named-type :name 'structure
- :supertypes '(structure t)
- :subclasses '(structure alien)))
-
-
- ;;;; Hairy and unknown types:
-
- ;;; The Hairy-Type represents anything too wierd to be described reasonably or
- ;;; to be useful, such as AND, NOT and SATISFIES and unknown types. We just
- ;;; remember the original type spec.
- ;;;
- (defstruct (hairy-type (:include ctype
- (:class-info (type-class-or-lose 'hairy))
- (:enumerable t))
- (:print-function %print-type))
- ;;
- ;; The Common Lisp type-specifier.
- (specifier nil :type t))
-
- (define-type-class hairy)
-
- (define-type-method (hairy :unparse) (x) (hairy-type-specifier x))
-
- (define-type-method (hairy :complex-subtypep-arg1 :complex-subtypep-arg2
- :complex-=)
- (type1 type2)
- (declare (ignore type1 type2))
- (values nil nil))
-
- (define-type-method (hairy :simple-intersection :complex-intersection)
- (type1 type2)
- (declare (ignore type2))
- (values type1 nil))
-
- (define-type-method (hairy :complex-union) (type1 type2)
- (make-union-type (list type1 type2)))
-
- (define-type-method (hairy :simple-= :simple-subtypep) (type1 type2)
- (if (equal (hairy-type-specifier type1)
- (hairy-type-specifier type2))
- (values t t)
- (values nil nil)))
-
- (def-type-translator not (&whole x type)
- (declare (ignore type))
- (make-hairy-type :specifier x))
-
- (def-type-translator satisfies (&whole x fun)
- (declare (ignore fun))
- (make-hairy-type :specifier x))
-
-
- ;;; An UNKNOWN-TYPE is a type not known to the type system (not yet defined).
- ;;; We make this distinction since we don't want to complain about types that
- ;;; are hairy but defined.
- ;;;
- (defstruct (unknown-type (:include hairy-type)))
-
-
- ;;;; Numeric types.
-
- ;;; A list of all the float formats, in order of decreasing precision.
- ;;;
- (eval-when (compile load eval)
- (defconstant float-formats
- '(long-float double-float single-float short-float)))
-
- ;;; The type of a float format.
- ;;;
- (deftype float-format () `(member ,@float-formats))
-
-
- ;;; The Numeric-Type is used to represent all numeric types, including things
- ;;; such as FIXNUM.
- (defstruct (numeric-type (:include ctype
- (:class-info (type-class-or-lose 'number)))
- (:print-function %print-type))
- ;;
- ;; The kind of numeric type we have. NIL if not specified (just NUMBER or
- ;; COMPLEX).
- (class nil :type (member integer rational float nil))
- ;;
- ;; Format for a float type. NIL if not specified or not a float. Formats
- ;; which don't exist in a given implementation don't appear here.
- (format nil :type (or float-format null))
- ;;
- ;; Is this a complex numeric type? Null if unknown (only in NUMBER.)
- (complexp :real :type (member :real :complex nil))
- ;;
- ;; The upper and lower bounds on the value. If null, there is no bound. If
- ;; a list of a number, the bound is exclusive. Integer types never have
- ;; exclusive bounds.
- (low nil :type (or number cons null))
- (high nil :type (or number cons null)))
-
-
- (define-type-class number)
-
- (define-type-method (number :simple-=) (type1 type2)
- (values
- (and (eq (numeric-type-class type1) (numeric-type-class type2))
- (eq (numeric-type-format type1) (numeric-type-format type2))
- (eq (numeric-type-complexp type1) (numeric-type-complexp type2))
- (equal (numeric-type-low type1) (numeric-type-low type2))
- (equal (numeric-type-high type1) (numeric-type-high type2)))
- t))
-
- (define-type-method (number :unparse) (type)
- (let* ((complexp (numeric-type-complexp type))
- (low (numeric-type-low type))
- (high (numeric-type-high type))
- (base (case (numeric-type-class type)
- (integer 'integer)
- (rational 'rational)
- (float (or (numeric-type-format type) 'float))
- (t 'real))))
- (let ((base+bounds
- (cond ((and (eq base 'integer) high low)
- (let ((high-count (logcount high))
- (high-length (integer-length high)))
- (cond ((= low 0)
- (cond ((= high 0) '(integer 0 0))
- ((= high 1) 'bit)
- ((and (= high-count high-length)
- (plusp high-length))
- `(unsigned-byte ,high-length))
- (t
- `(mod ,(1+ high)))))
- ((and (= low vm:target-most-negative-fixnum)
- (= high vm:target-most-positive-fixnum))
- 'fixnum)
- ((and (= low (lognot high))
- (= high-count high-length)
- (> high-count 0))
- `(signed-byte ,(1+ high-length)))
- (t
- `(integer ,low ,high)))))
- (high `(,base ,(or low '*) ,high))
- (low
- (if (and (eq base 'integer) (= low 0))
- 'unsigned-byte
- `(,base ,low)))
- (t base))))
- (ecase complexp
- (:real
- base+bounds)
- (:complex
- (if (eq base+bounds 'real)
- 'complex
- `(complex ,base+bounds)))
- ((nil)
- (assert (eq base+bounds 'real))
- 'number)))))
-
- ;;; Numeric-Bound-Test -- Internal
- ;;;
- ;;; Return true if X is "less than or equal" to Y, taking open bounds into
- ;;; consideration. Closed is the predicate used to test the bound on a closed
- ;;; interval (e.g. <=), and Open is the predicate used on open bounds (e.g. <).
- ;;; Y is considered to be the outside bound, in the sense that if it is
- ;;; infinite (NIL), then the test suceeds, whereas if X is infinite, then the
- ;;; test fails (unless Y is also infinite).
- ;;;
- ;;; This is for comparing bounds of the same kind, e.g. upper and upper.
- ;;; Use Numeric-Bound-Test* for different kinds of bounds.
- ;;;
- (defmacro numeric-bound-test (x y closed open)
- `(cond ((not ,y) t)
- ((not ,x) nil)
- ((consp ,x)
- (if (consp ,y)
- (,closed (car ,x) (car ,y))
- (,closed (car ,x) ,y)))
- (t
- (if (consp ,y)
- (,open ,x (car ,y))
- (,closed ,x ,y)))))
-
-
- ;;; Numeric-Bound-Test* -- Internal
- ;;;
- ;;; Used to compare upper and lower bounds. This is different from the
- ;;; same-bound case:
- ;;; -- Since X = NIL is -infinity, whereas y = NIL is +infinity, we return true
- ;;; if *either* arg is NIL.
- ;;; -- an open inner bound is "greater" and also squeezes the interval, causing
- ;;; us to use the Open test for those cases as well.
- ;;;
- (defmacro numeric-bound-test* (x y closed open)
- `(cond ((not ,y) t)
- ((not ,x) t)
- ((consp ,x)
- (if (consp ,y)
- (,open (car ,x) (car ,y))
- (,open (car ,x) ,y)))
- (t
- (if (consp ,y)
- (,open ,x (car ,y))
- (,closed ,x ,y)))))
-
-
- ;;; Numeric-Bound-Max -- Internal
- ;;;
- ;;; Return whichever of the numeric bounds X and Y is "maximal" according to
- ;;; the predicates Closed (e.g. >=) and Open (e.g. >). This is only meaningful
- ;;; for maximizing like bounds, i.e. upper and upper. If Max-P is true, then
- ;;; we return NIL if X or Y is NIL, otherwise we return the other arg.
- ;;;
- (defmacro numeric-bound-max (x y closed open max-p)
- (once-only ((n-x x)
- (n-y y))
- `(cond ((not ,n-x) ,(if max-p nil n-y))
- ((not ,n-y) ,(if max-p nil n-x))
- ((consp ,n-x)
- (if (consp ,n-y)
- (if (,closed (car ,n-x) (car ,n-y)) ,n-x ,n-y)
- (if (,open (car ,n-x) ,n-y) ,n-x ,n-y)))
- (t
- (if (consp ,n-y)
- (if (,open (car ,n-y) ,n-x) ,n-y ,n-x)
- (if (,closed ,n-y ,n-x) ,n-y ,n-x))))))
-
-
- (define-type-method (number :simple-subtypep) (type1 type2)
- (let ((class1 (numeric-type-class type1))
- (class2 (numeric-type-class type2))
- (complexp2 (numeric-type-complexp type2))
- (format2 (numeric-type-format type2))
- (low1 (numeric-type-low type1))
- (high1 (numeric-type-high type1))
- (low2 (numeric-type-low type2))
- (high2 (numeric-type-high type2)))
- ;;
- ;; If one is complex and the other isn't, they are disjoint.
- (cond ((not (or (eq (numeric-type-complexp type1) complexp2)
- (null complexp2)))
- (values nil t))
- ;;
- ;; If the classes are specified and different, the types are
- ;; disjoint unless type2 is rational and type1 is integer.
- ((not (or (eq class1 class2) (null class2)
- (and (eq class1 'integer) (eq class2 'rational))))
- (values nil t))
- ;;
- ;; If the float formats are specified and different, the types
- ;; are disjoint.
- ((not (or (eq (numeric-type-format type1) format2)
- (null format2)))
- (values nil t))
- ;;
- ;; Check the bounds.
- ((and (numeric-bound-test low1 low2 >= >)
- (numeric-bound-test high1 high2 <= <))
- (values t t))
- (t
- (values nil t)))))
-
-
- ;;; NUMERIC-TYPES-ADJACENT -- Internal
- ;;;
- ;;; If the high bound of Low is adjacent to the low bound of High, then
- ;;; return T, otherwise NIL.
- ;;;
- (defun numeric-types-adjacent (low high)
- (let ((low-bound (numeric-type-high low))
- (high-bound (numeric-type-low high)))
- (cond ((not (and low-bound high-bound)) nil)
- ((consp low-bound)
- (eql (car low-bound) high-bound))
- ((consp high-bound)
- (eql (car high-bound) low-bound))
- ((and (eq (numeric-type-class low) 'integer)
- (eq (numeric-type-class high) 'integer))
- (eql (1+ low-bound) high-bound))
- (t
- nil))))
-
-
- ;;; NUMBER :SIMPLE-UNION method -- Internal
- ;;;
- ;;; Return the a numeric type that is a supertype for both type1 and type2.
- ;;;
- ;;; ### Note: we give up early, so keep from dropping lots of information on
- ;;; the floor by returning overly general types.
- ;;;
- (define-type-method (number :simple-union) (type1 type2)
- (declare (type numeric-type type1 type2))
- (cond ((csubtypep type1 type2) type2)
- ((csubtypep type2 type1) type1)
- (t
- (let ((class1 (numeric-type-class type1))
- (format1 (numeric-type-format type1))
- (complexp1 (numeric-type-complexp type1))
- (class2 (numeric-type-class type2))
- (format2 (numeric-type-format type2))
- (complexp2 (numeric-type-complexp type2)))
- (when (and (eq class1 class2)
- (eq format1 format2)
- (eq complexp1 complexp2)
- (or (numeric-types-intersect type1 type2)
- (numeric-types-adjacent type1 type2)
- (numeric-types-adjacent type2 type1)))
- (make-numeric-type
- :class class1
- :format format1
- :complexp complexp1
- :low (numeric-bound-max (numeric-type-low type1)
- (numeric-type-low type2)
- < <= t)
- :high (numeric-bound-max (numeric-type-high type1)
- (numeric-type-high type2)
- > >= t)))))))
-
-
- (def-builtin-type 'number
- (make-numeric-type :complexp nil))
-
-
- (deftype bit () '(integer 0 1))
- (deftype fixnum ()
- '(integer #.vm:target-most-negative-fixnum
- #.vm:target-most-positive-fixnum))
- (deftype bignum () '(and integer (not fixnum)))
-
- (def-type-translator complex (&optional spec)
- (if (eq spec '*)
- (make-numeric-type :complexp :complex)
- (let ((type (specifier-type spec)))
- (unless (numeric-type-p type)
- (error "Component type for Complex is not numeric: ~S." spec))
- (when (eq (numeric-type-complexp type) :complex)
- (error "Component type for Complex is complex: ~S." spec))
-
- (let ((res (copy-numeric-type type)))
- (setf (numeric-type-complexp res) :complex)
- res))))
-
-
- ;;; Check-Bound -- Internal
- ;;;
- ;;; Check that X is a well-formed numeric bound of the specified Type.
- ;;; If X is *, return NIL, otherwise return the bound.
- ;;;
- (defmacro check-bound (x type)
- `(cond ((eq ,x '*) nil)
- ((or (typep ,x ',type)
- (and (consp ,x) (typep (car ,x) ',type) (null (cdr ,x))))
- ,x)
- (t
- (error "Bound is not *, a ~A or a list of a ~A: ~S" ',type ',type ,x))))
-
- (def-type-translator integer (&optional low high)
- (let* ((l (check-bound low integer))
- (lb (if (consp l) (1+ (car l)) l))
- (h (check-bound high integer))
- (hb (if (consp h) (1- (car h)) h)))
- (when (and hb lb (< hb lb))
- (error "Lower bound ~S is greater than upper bound ~S." l h))
- (make-numeric-type :class 'integer :complexp :real
- :enumerable (not (null (and l h)))
- :low lb
- :high hb)))
-
- (deftype mod (n)
- (unless (and (integerp n) (> n 0))
- (error "Bad N specified for MOD type specifier: ~S." n))
- `(integer 0 ,(1- n)))
-
- (deftype signed-byte (&optional s)
- (cond ((eq s '*) 'integer)
- ((and (integerp s) (> s 1))
- (let ((bound (ash 1 (1- s))))
- `(integer ,(- bound) ,(1- bound))))
- (t
- (error "Bad size specified for SIGNED-BYTE type specifier: ~S." s))))
-
- (deftype unsigned-byte (&optional s)
- (cond ((eq s '*) '(integer 0))
- ((and (integerp s) (> s 0))
- `(integer 0 ,(1- (ash 1 s))))
- (t
- (error "Bad size specified for UNSIGNED-BYTE type specifier: ~S." s))))
-
-
- (defmacro def-bounded-type (type class format)
- `(def-type-translator ,type (&optional low high)
- (let ((lb (check-bound low ,type))
- (hb (check-bound high ,type)))
- (unless (numeric-bound-test* lb hb <= <)
- (error "Lower bound ~S is not less than upper bound ~S." low high))
- (make-numeric-type :class ',class :format ',format :low lb :high hb))))
-
- (def-bounded-type rational rational nil)
- (def-bounded-type float float nil)
- (def-bounded-type real nil nil)
-
- (defmacro define-float-format (f)
- `(def-bounded-type ,f float ,f))
-
- (define-float-format short-float)
- (define-float-format single-float)
- (define-float-format double-float)
- (define-float-format long-float)
-
- (deftype ratio () '(and rational (not integer)))
-
-
- (defun numeric-types-intersect (type1 type2)
- (declare (type numeric-type type1 type2))
- (let* ((class1 (numeric-type-class type1))
- (class2 (numeric-type-class type2))
- (complexp1 (numeric-type-complexp type1))
- (complexp2 (numeric-type-complexp type2))
- (format1 (numeric-type-format type1))
- (format2 (numeric-type-format type2))
- (low1 (numeric-type-low type1))
- (high1 (numeric-type-high type1))
- (low2 (numeric-type-low type2))
- (high2 (numeric-type-high type2)))
- ;;
- ;; If one is complex and the other isn't, then they are disjoint.
- (cond ((not (or (eq complexp1 complexp2)
- (null complexp1) (null complexp2)))
- nil)
- ;;
- ;; If either type is a float, then the other must either be specified
- ;; to be a float or unspecified. Otherwise, they are disjoint.
- ((and (eq class1 'float) (not (member class2 '(float nil)))) nil)
- ((and (eq class2 'float) (not (member class1 '(float nil)))) nil)
- ;;
- ;; If the float formats are specified and different, the types
- ;; are disjoint.
- ((not (or (eq format1 format2) (null format1) (null format2)))
- nil)
- (t
- ;;
- ;; Check the bounds. This is a bit odd because we must always have
- ;; the outer bound of the interval as the second arg.
- (if (numeric-bound-test high1 high2 <= <)
- (or (and (numeric-bound-test low1 low2 >= >)
- (numeric-bound-test* low1 high2 <= <))
- (and (numeric-bound-test low2 low1 >= >)
- (numeric-bound-test* low2 high1 <= <)))
- (or (and (numeric-bound-test* low2 high1 <= <)
- (numeric-bound-test low2 low1 >= >))
- (and (numeric-bound-test high2 high1 <= <)
- (numeric-bound-test* high2 low1 >= >))))))))
-
-
- ;;; Round-Numeric-Bound -- Internal
- ;;;
- ;;; Take the numeric bound X and convert it into something that can be used
- ;;; as a bound in a numeric type with the specified Class and Format. If up-p
- ;;; is true, then we round up as needed, otherwise we round down. Up-p true
- ;;; implies that X is a lower bound, i.e. (N) > N.
- ;;;
- ;;; This is used by Numeric-Type-Intersection to mash the bound into the
- ;;; appropriate type number. X may only be a float when Class is Float.
- ;;;
- ;;; ### Note: it is possible for the coercion to a float to overflow or
- ;;; underflow. This happens when the bound doesn't fit in the specified
- ;;; format. In this case, we should really return the appropriate
- ;;; {Most | Least}-{Positive | Negative}-XXX-Float float of desired format.
- ;;; But these conditions aren't currently signalled in any useful way.
- ;;;
- ;;; Also, when converting an open rational bound into a float we should
- ;;; probably convert it to a closed bound of the closest float in the specified
- ;;; format. In general, open float bounds are fucked.
- ;;;
- (defun round-numeric-bound (x class format up-p)
- (if x
- (let ((cx (if (consp x) (car x) x)))
- (ecase class
- ((nil rational) x)
- (integer
- (if (and (consp x) (integerp cx))
- (if up-p (1+ cx) (1- cx))
- (if up-p (ceiling cx) (floor cx))))
- (float
- (let ((res (if format (coerce cx format) (float cx))))
- (if (consp x) (list res) res)))))
- nil))
-
-
- ;;; Number :Simple-Intersection type method -- Internal
- ;;;
- ;;; Handle the case of Type-Intersection on two numeric types. We use
- ;;; Types-Intersect to throw out the case of types with no intersection. If an
- ;;; attribute in Type1 is unspecified, then we use Type2's attribute, which
- ;;; must be at least as restrictive. If the types intersect, then the only
- ;;; attributes that can be specified and different are the class and the
- ;;; bounds.
- ;;;
- ;;; When the class differs, we use the more restrictive class. The only
- ;;; interesting case is rational/integer, since rational includes integer.
- ;;;
- ;;; We make the result lower (upper) bound the maximum (minimum) of the
- ;;; argument lower (upper) bounds. We convert the bounds into the
- ;;; appropriate numeric type before maximizing. This avoids possible confusion
- ;;; due to mixed-type comparisons (but I think the result is the same).
- ;;;
- (define-type-method (number :simple-intersection) (type1 type2)
- (declare (type numeric-type type1 type2))
- (if (numeric-types-intersect type1 type2)
- (let* ((class1 (numeric-type-class type1))
- (class2 (numeric-type-class type2))
- (class (ecase class1
- ((nil) class2)
- ((integer float) class1)
- (rational (if (eq class2 'integer) 'integer 'rational))))
- (format (or (numeric-type-format type1)
- (numeric-type-format type2))))
- (values
- (make-numeric-type
- :class class
- :format format
- :complexp (or (numeric-type-complexp type1)
- (numeric-type-complexp type2))
- :low (numeric-bound-max
- (round-numeric-bound (numeric-type-low type1)
- class format t)
- (round-numeric-bound (numeric-type-low type2)
- class format t)
- >= > nil)
- :high (numeric-bound-max
- (round-numeric-bound (numeric-type-high type1)
- class format nil)
- (round-numeric-bound (numeric-type-high type2)
- class format nil)
- <= < nil))
- t))
- (values *empty-type* t)))
-
-
- ;;; Float-Format-Max -- Interface
- ;;;
- ;;; Given two float formats, return the one with more precision. If either
- ;;; one is null, return NIL.
- ;;;
- (defun float-format-max (f1 f2)
- (when (and f1 f2)
- (dolist (f float-formats (error "Bad float format: ~S." f1))
- (when (or (eq f f1) (eq f f2))
- (return f)))))
-
-
- ;;; Numeric-Contagion -- Interface
- ;;;
- ;;; Return the result of an operation on Type1 and Type2 according to the
- ;;; rules of numeric contagion. This is always NUMBER, some float format
- ;;; (possibly complex) or RATIONAL. Due to rational canonicalization, there
- ;;; isn't much we can do here with integers or rational complex numbers.
- ;;;
- ;;; If either argument is not a Numeric-Type, then return NUMBER. This is
- ;;; useful mainly for allowing types that are technically numbers, but not a
- ;;; Numeric-Type.
- ;;;
- (defun numeric-contagion (type1 type2)
- (if (and (numeric-type-p type1) (numeric-type-p type2))
- (let ((class1 (numeric-type-class type1))
- (class2 (numeric-type-class type2))
- (format1 (numeric-type-format type1))
- (format2 (numeric-type-format type2))
- (complexp1 (numeric-type-complexp type1))
- (complexp2 (numeric-type-complexp type2)))
- (cond ((or (null complexp1)
- (null complexp2))
- (specifier-type 'number))
- ((eq class1 'float)
- (make-numeric-type
- :class 'float
- :format (if (eq class2 'float)
- (float-format-max format1 format2)
- format1)
- :complexp (if (or (eq complexp1 :complex)
- (eq complexp2 :complex))
- :complex
- :real)))
- ((eq class2 'float) (numeric-contagion type2 type1))
- ((and (eq complexp1 :real) (eq complexp2 :real))
- (make-numeric-type
- :class (and class1 class2 'rational)
- :complexp :real))
- (t
- (specifier-type 'number))))
- (specifier-type 'number)))
-
-
- ;;;; Array types:
-
- ;;; The Array-Type is used to represent all array types, including things such
- ;;; as SIMPLE-STRING.
- ;;;
- (defstruct (array-type (:include ctype
- (:class-info (type-class-or-lose 'array)))
- (:print-function %print-type))
- ;;
- ;; The dimensions of the array. * if unspecified. If a dimension is
- ;; unspecified, it is *.
- (dimensions '* :type (or list (member *)))
- ;;
- ;; Is this not a simple array type?
- (complexp '* :type (member t nil *))
- ;;
- ;; The element type as originally specified.
- (element-type (required-argument) :type ctype)
- ;;
- ;; The element type as it is specialized in this implementation.
- (specialized-element-type *wild-type* :type ctype))
-
- (define-type-class array)
-
-
- ;;; Specialized-Element-Type-Maybe -- Internal
- ;;;
- ;;; What this does depends on the setting of the
- ;;; *use-implementation-types* switch. If true, return the specialized element
- ;;; type, otherwise return the original element type.
- ;;;
- (defun specialized-element-type-maybe (type)
- (declare (type array-type type))
- (if *use-implementation-types*
- (array-type-specialized-element-type type)
- (array-type-element-type type)))
-
-
- (define-type-method (array :simple-=) (type1 type2)
- (values (and (equal (array-type-dimensions type1)
- (array-type-dimensions type2))
- (eq (array-type-complexp type1)
- (array-type-complexp type2))
- (type= (specialized-element-type-maybe type1)
- (specialized-element-type-maybe type2)))
- t))
-
-
- (define-type-method (array :unparse) (type)
- (let ((dims (array-type-dimensions type))
- (eltype (type-specifier (array-type-element-type type)))
- (complexp (array-type-complexp type)))
- (cond ((eq dims '*)
- (if (eq eltype '*)
- (if complexp 'array 'simple-array)
- (if complexp `(array ,eltype) `(simple-array ,eltype))))
- ((= (length dims) 1)
- (if complexp
- (if (eq (car dims) '*)
- (case eltype
- (bit 'bit-vector)
- (base-char 'base-string)
- (* 'vector)
- (t `(vector ,eltype)))
- (case eltype
- (bit `(bit-vector ,(car dims)))
- (base-char `(base-string ,(car dims)))
- (t `(vector ,eltype ,(car dims)))))
- (if (eq (car dims) '*)
- (case eltype
- (bit 'simple-bit-vector)
- (base-char 'simple-base-string)
- ((t) 'simple-vector)
- (t `(simple-array ,eltype (*))))
- (case eltype
- (bit `(simple-bit-vector ,(car dims)))
- (base-char `(simple-base-string ,(car dims)))
- ((t) `(simple-vector ,(car dims)))
- (t `(simple-array ,eltype ,dims))))))
- (t
- (if complexp
- `(array ,eltype ,dims)
- `(simple-array ,eltype ,dims))))))
-
-
- (define-type-method (array :simple-subtypep) (type1 type2)
- (let ((dims1 (array-type-dimensions type1))
- (dims2 (array-type-dimensions type2))
- (complexp2 (array-type-complexp type2)))
- ;;
- ;; See if dimensions are compatible.
- (cond ((not (or (eq dims2 '*)
- (and (not (eq dims1 '*))
- (= (length dims1) (length dims2))
- (every #'(lambda (x y)
- (or (eq y '*) (eql x y)))
- dims1 dims2))))
- (values nil t))
- ;;
- ;; See if complexp is compatible.
- ((not (or (eq complexp2 '*)
- (eq (array-type-complexp type1) complexp2)))
- (values nil t))
- ;;
- ;; If the type2 eltype is wild, we win. Otherwise, the types must be
- ;; identical.
- ((or (eq (array-type-element-type type2) *wild-type*)
- (type= (specialized-element-type-maybe type1)
- (specialized-element-type-maybe type2)))
- (values t t))
- (t
- (values nil t)))))
-
-
- (defun array-types-intersect (type1 type2)
- (declare (type array-type type1 type2))
- (let ((dims1 (array-type-dimensions type1))
- (dims2 (array-type-dimensions type2))
- (complexp1 (array-type-complexp type1))
- (complexp2 (array-type-complexp type2)))
- ;;
- ;; See if dimensions are compatible.
- (cond ((not (or (eq dims1 '*) (eq dims2 '*)
- (and (= (length dims1) (length dims2))
- (every #'(lambda (x y)
- (or (eq x '*) (eq y '*) (= x y)))
- dims1 dims2))))
- (values nil t))
- ;;
- ;; See if complexp is compatible.
- ((not (or (eq complexp1 '*) (eq complexp2 '*)
- (eq complexp1 complexp2)))
- (values nil t))
- ;;
- ;; If either element type is wild, then they intersect. Otherwise,
- ;; the types must be identical.
- ((or (eq (array-type-element-type type1) *wild-type*)
- (eq (array-type-element-type type2) *wild-type*)
- (type= (specialized-element-type-maybe type1)
- (specialized-element-type-maybe type2)))
-
- (values t t))
- (t
- (values nil t)))))
-
-
- (define-type-method (array :simple-intersection) (type1 type2)
- (declare (type array-type type1 type2))
- (if (array-types-intersect type1 type2)
- (let ((dims1 (array-type-dimensions type1))
- (dims2 (array-type-dimensions type2))
- (complexp1 (array-type-complexp type1))
- (complexp2 (array-type-complexp type2))
- (eltype1 (array-type-element-type type1))
- (eltype2 (array-type-element-type type2)))
- (values
- (specialize-array-type
- (make-array-type
- :dimensions (cond ((eq dims1 '*) dims2)
- ((eq dims2 '*) dims1)
- (t
- (mapcar #'(lambda (x y) (if (eq x '*) y x))
- dims1 dims2)))
- :complexp (if (eq complexp1 '*) complexp2 complexp1)
- :element-type (if (eq eltype1 *wild-type*) eltype2 eltype1)))
- t))
- (values *empty-type* t)))
-
-
- ;;; Check-Array-Dimensions -- Internal
- ;;;
- ;;; Check a supplied dimension list to determine if it is legal.
- ;;;
- (defun check-array-dimensions (dims)
- (typecase dims
- ((member *) dims)
- (integer
- (when (minusp dims)
- (error "Arrays can't have a negative number of dimensions: ~D." dims))
- (when (>= dims array-rank-limit)
- (error "Array type has too many dimensions: ~S." dims))
- (make-list dims :initial-element '*))
- (list
- (when (>= (length dims) array-rank-limit)
- (error "Array type has too many dimensions: ~S." dims))
- (dolist (dim dims)
- (unless (eq dim '*)
- (unless (and (integerp dim)
- (>= dim 0) (< dim array-dimension-limit))
- (error "Bad dimension in array type: ~S." dim))))
- dims)
- (t
- (error "Array dimensions is not a list, integer or *:~% ~S"
- dims))))
-
- (def-type-translator array (&optional element-type dimensions)
- (specialize-array-type
- (make-array-type :dimensions (check-array-dimensions dimensions)
- :element-type (specifier-type element-type))))
-
- (def-type-translator simple-array (&optional element-type dimensions)
- (specialize-array-type
- (make-array-type :dimensions (check-array-dimensions dimensions)
- :element-type (specifier-type element-type)
- :complexp nil)))
-
- (deftype vector (&optional element-type size)
- `(array ,element-type (,size)))
-
- (deftype simple-vector (&optional size)
- `(simple-array t (,size)))
-
- (deftype base-string (&optional size)
- `(array base-char (,size)))
- (deftype simple-base-string (&optional size)
- `(simple-array base-char (,size)))
- (deftype string (&optional size)
- `(or (array character (,size))
- (base-string ,size)))
- (deftype simple-string (&optional size)
- `(or (simple-array character (,size))
- (simple-base-string ,size)))
-
- (deftype bit-vector (&optional size)
- `(array bit (,size)))
-
- (deftype simple-bit-vector (&optional size)
- `(simple-array bit (,size)))
-
-
- ;;;; Member types.
-
- ;;; The Member-Type represents uses of the MEMBER type specifier. We bother
- ;;; with this at this level because MEMBER types are fairly important and union
- ;;; and intersection are well defined.
-
- (defstruct (member-type (:include ctype
- (:class-info (type-class-or-lose 'member))
- (:enumerable t))
- (:print-function %print-type))
- ;;
- ;; The things in the set, with no duplications.
- (members nil :type list))
-
-
- (define-type-class member)
-
- (define-type-method (member :unparse) (type)
- (let ((members (member-type-members type)))
- (if (equal members '(nil))
- 'null
- `(member ,@members))))
-
- (define-type-method (member :simple-subtypep) (type1 type2)
- (values (subsetp (member-type-members type1) (member-type-members type2))
- t))
-
-
- (define-type-method (member :complex-subtypep-arg1) (type1 type2)
- (block PUNT
- (values (every-type-op ctypep type2 (member-type-members type1)
- :list-first t)
- t)))
-
- ;;; We punt if the odd type is enumerable and intersects with the member type.
- ;;; If not enumerable, then it is definitely not a subtype of the member type.
- ;;;
- (define-type-method (member :complex-subtypep-arg2) (type1 type2)
- (cond ((not (type-enumerable type1)) (values nil t))
- ((types-intersect type1 type2) (values nil nil))
- (t
- (values nil t))))
-
- (define-type-method (member :simple-intersection) (type1 type2)
- (let ((mem1 (member-type-members type1))
- (mem2 (member-type-members type2)))
- (values (cond ((subsetp mem1 mem2) type1)
- ((subsetp mem2 mem1) type2)
- (t
- (let ((res (intersection mem1 mem2)))
- (if res
- (make-member-type :members res)
- *empty-type*))))
- t)))
-
- (define-type-method (member :complex-intersection) (type1 type2)
- (block PUNT
- (collect ((members))
- (let ((mem2 (member-type-members type2)))
- (dolist (member mem2)
- (multiple-value-bind (val win)
- (ctypep member type1)
- (unless win
- (return-from PUNT (values type2 nil)))
- (when val (members member))))
-
- (values (cond ((subsetp mem2 (members)) type2)
- ((null (members)) *empty-type*)
- (t
- (make-member-type :members (members))))
- t)))))
-
-
- ;;; We don't need a :COMPLEX-UNION, since the only interesting case is a union
- ;;; type, and the member/union interaction is handled by the union type
- ;;; method.
- (define-type-method (member :simple-union) (type1 type2)
- (let ((mem1 (member-type-members type1))
- (mem2 (member-type-members type2)))
- (cond ((subsetp mem1 mem2) type2)
- ((subsetp mem2 mem1) type1)
- (t
- (make-member-type :members (union mem1 mem2))))))
-
-
- (define-type-method (member :simple-=) (type1 type2)
- (let ((mem1 (member-type-members type1))
- (mem2 (member-type-members type2)))
- (values (and (subsetp mem1 mem2) (subsetp mem2 mem1))
- t)))
-
- (define-type-method (member :complex-=) (type1 type2)
- (if (type-enumerable type1)
- (multiple-value-bind (val win)
- (csubtypep type2 type1)
- (if (or val (not win))
- (values nil nil)
- (values nil t)))
- (values nil t)))
-
-
- (def-type-translator member (&rest members)
- (let ((mem (remove-duplicates members)))
- (if mem
- (make-member-type :members mem)
- *empty-type*)))
-
-
- ;;;; Structure types.
-
- ;;; The Structure type is used to represent the type of things known to be
- ;;; structures.
- ;;;
- (defstruct (structure-type
- (:include ctype
- (:class-info (type-class-or-lose 'structure)))
- (:print-function %print-type))
- ;;
- ;; Name of the structure type.
- name)
-
- (define-type-class structure)
-
- ;;; The (info type structure-info ...) may be NIL if the type has been
- ;;; undefined since the specifier was parsed.
- ;;;
- (define-type-method (structure :simple-subtypep) (type1 type2)
- (let ((name2 (structure-type-name type2)))
- (if (eq (structure-type-name type1) name2)
- (values t t)
- (let ((info1 (info type structure-info (structure-type-name type1))))
- (if info1
- (if (member name2 (c::dd-includes info1))
- (values t t)
- (values nil t))
- (values nil nil))))))
-
- (define-type-method (structure :complex-subtypep-arg2) (type1 type2)
- (declare (type structure-type type2))
- (values (or (eq type1 *wild-type*)
- (and (eq (type-class-name (type-class-info type1)) 'alien)
- (eq (structure-type-name type2) 'alien-value)))
- t))
-
- (define-type-method (structure :unparse) (type)
- (structure-type-name type))
-
- (define-type-method (structure :simple-=) (type1 type2)
- (values (eq (structure-type-name type1) (structure-type-name type2))
- t))
-
-
- ;;;; Union types:
-
- ;;; The Union-Type represents uses of the OR type specifier which can't be
- ;;; canonicalized to something simpler. Canonical form:
- ;;;
- ;;; 1] There is never more than one Member-Type component.
- ;;; 2] There are never any Union-Type components.
- ;;;
- (defstruct (union-type (:include ctype
- (:class-info (type-class-or-lose 'union)))
- (:constructor %make-union-type (enumerable types))
- (:print-function %print-type))
- ;;
- ;; The types in the union.
- (types nil :type list))
-
-
- ;;; MAKE-UNION-TYPE -- Internal
- ;;;
- ;;; Make a union type from the specifier types, setting ENUMERABLE in the
- ;;; result if all are enumerable.
- ;;;
- (defun make-union-type (types)
- (declare (list types))
- (%make-union-type (every #'type-enumerable types) types))
-
-
- (define-type-class union)
-
-
- ;;; If List, then return that, otherwise the OR of the component types.
- ;;;
- (define-type-method (union :unparse) (type)
- (declare (type ctype type))
- (if (type= type (specifier-type 'list))
- 'list
- `(or ,@(mapcar #'type-specifier (union-type-types type)))))
-
-
-
- ;;; Two union types are equal if every type in one is equal to some type in the
- ;;; other.
- ;;;
- (define-type-method (union :simple-=) (type1 type2)
- (block PUNT
- (let ((types1 (union-type-types type1))
- (types2 (union-type-types type2)))
- (values (and (dolist (type1 types1 t)
- (unless (any-type-op type= type1 types2)
- (return nil)))
- (dolist (type2 types2 t)
- (unless (any-type-op type= type2 types1)
- (return nil))))
- t))))
-
-
- ;;; Similarly, a union type is a subtype of another if every element of Type1
- ;;; is a subtype of some element of Type2.
- ;;;
- (define-type-method (union :simple-subtypep) (type1 type2)
- (block PUNT
- (let ((types2 (union-type-types type2)))
- (values (dolist (type1 (union-type-types type1) t)
- (unless (any-type-op csubtypep type1 types2)
- (return nil)))
- t))))
-
-
- (define-type-method (union :complex-subtypep-arg1) (type1 type2)
- (block PUNT
- (values (every-type-op csubtypep type2 (union-type-types type1)
- :list-first t)
- t)))
-
- (define-type-method (union :complex-subtypep-arg2) (type1 type2)
- (block PUNT
- (values (any-type-op csubtypep type1 (union-type-types type2)) t)))
-
-
- (define-type-method (union :complex-union) (type1 type2)
- (let* ((class1 (type-class-info type1))
- (union-meth-1 (type-class-simple-union class1)))
- (collect ((res))
- (let ((this-type type1))
- (dolist (type (union-type-types type2)
- (if (res)
- (make-union-type (cons this-type (res)))
- this-type))
- (cond ((and union-meth-1
- (eq (type-class-info type) class1))
- (let ((union (funcall union-meth-1 this-type type)))
- (if union
- (setq this-type union)
- (res type))))
- ((csubtypep type this-type))
- ((csubtypep type1 type) (return type2))
- (t
- (res type))))))))
-
- ;;; For the union of union types, we let the :COMPLEX-UNION method do the work.
- ;;;
- (define-type-method (union :simple-union) (type1 type2)
- (let ((res type1))
- (dolist (t2 (union-type-types type2) res)
- (setq res (type-union res t2)))))
-
-
- (define-type-method (union :simple-intersection :complex-intersection)
- (type1 type2)
- (let ((res *empty-type*)
- (win t))
- (dolist (type (union-type-types type2) (values res win))
- (multiple-value-bind (int w)
- (type-intersection type1 type)
- (setq res (type-union res int))
- (unless w (setq win nil))))))
-
-
- (def-type-translator or (&rest types)
- (reduce #'type-union
- (mapcar #'specifier-type types)
- :initial-value *empty-type*))
-
-
- ;;; We don't actually have intersection types, since the result of
- ;;; reasonable type intersections is always describable as a union of simple
- ;;; types. If something is too hairy to fit this mold, then we make a hairy
- ;;; type.
- (def-type-translator and (&whole spec &rest types)
- (let ((res *wild-type*))
- (dolist (type types res)
- (let ((ctype (specifier-type type)))
- (multiple-value-bind (int win)
- (type-intersection res ctype)
- (unless win
- (return (make-hairy-type :specifier spec)))
- (setq res int))))))
-
-
- ;;;; Alien-type types
-
- (defstruct (alien-type-type
- (:include ctype
- (:class-info (type-class-or-lose 'alien)))
- (:print-function %print-type)
- (:constructor %make-alien-type-type (alien-type)))
- (alien-type nil :type alien-type))
-
- (define-type-class alien)
-
- (define-type-method (alien :unparse) (type)
- `(alien ,(unparse-alien-type (alien-type-type-alien-type type))))
-
- (define-type-method (alien :simple-subtypep) (type1 type2)
- (values (alien-subtype-p (alien-type-type-alien-type type1)
- (alien-type-type-alien-type type2))
- t))
-
- (define-type-method (alien :simple-=) (type1 type2)
- (let ((alien-type-1 (alien-type-type-alien-type type1))
- (alien-type-2 (alien-type-type-alien-type type2)))
- (values (or (eq alien-type-1 alien-type-2)
- (alien-type-= alien-type-1 alien-type-2))
- t)))
-
- (define-type-method (alien :complex-intersection) (type1 type2)
- (vanilla-intersection type1 type2))
-
- (def-type-translator alien (&optional (alien-type nil))
- (typecase alien-type
- (null
- (make-alien-type-type))
- (alien-type
- (make-alien-type-type alien-type))
- (t
- (make-alien-type-type (parse-alien-type alien-type)))))
-
- (defun make-alien-type-type (&optional alien-type)
- (if alien-type
- (let ((lisp-rep-type (compute-lisp-rep-type alien-type)))
- (if lisp-rep-type
- (specifier-type lisp-rep-type)
- (%make-alien-type-type alien-type)))
- *universal-type*))
-
-
- ;;; TYPE-DIFFERENCE -- Interface
- ;;;
- ;;; Return the type that describes all objects that are in X but not in Y.
- ;;; If we can't determine this type, then return NIL.
- ;;;
- ;;; For now, we only are clever dealing with union and member types. If
- ;;; either type is not a union type, then we pretend that it is a union of just
- ;;; one type. What we do is remove from X all the types that are a subtype any
- ;;; type in Y. If any type in X intersects with a type in Y but is not a
- ;;; subtype, then we give up.
- ;;;
- ;;; We must also special-case any member type that appears in the union. We
- ;;; remove from X's members all objects that are TYPEP to Y. If Y has any
- ;;; members, we must be careful that none of those members are CTYPEP to any
- ;;; of Y's non-member types. We give up in this case, since to compute that
- ;;; difference we would have to break the type from X into some collection of
- ;;; types that represents the type without that particular element. This seems
- ;;; too hairy to be worthwhile, given its low utility.
- ;;;
- (defun type-difference (x y)
- (let ((x-types (if (union-type-p x) (union-type-types x) (list x)))
- (y-types (if (union-type-p y) (union-type-types y) (list y))))
- (collect ((res))
- (dolist (x-type x-types)
- (if (member-type-p x-type)
- (collect ((members))
- (dolist (mem (member-type-members x-type))
- (multiple-value-bind (val win)
- (ctypep mem y)
- (unless win (return-from type-difference nil))
- (unless val
- (members mem))))
- (when (members)
- (res (make-member-type :members (members)))))
- (dolist (y-type y-types (res x-type))
- (multiple-value-bind (val win)
- (csubtypep x-type y-type)
- (unless win (return-from type-difference nil))
- (when val (return))
- (when (types-intersect x-type y-type)
- (return-from type-difference nil))))))
-
- (let ((y-mem (find-if #'member-type-p y-types)))
- (when y-mem
- (let ((members (member-type-members y-mem)))
- (dolist (x-type x-types)
- (unless (member-type-p x-type)
- (dolist (member members)
- (multiple-value-bind (val win)
- (ctypep member x-type)
- (when (or (not win) val)
- (return-from type-difference nil)))))))))
-
- (cond ((null (res)) *empty-type*)
- ((null (rest (res))) (first (res)))
- (t
- (make-union-type (res)))))))
-
-
- ;;;; Miscellaneous interfaces:
-
- ;;; CTypep -- Internal
- ;;;
- ;;; If Type is a type that we can do a compile-time test on, then return the
- ;;; whether the object is of that type as the first value and second value
- ;;; true. Otherwise return NIL, NIL.
- ;;;
- ;;; We give up on unknown types, pick off FUNCTION and UNION types. For
- ;;; structure types, we require that the type be defined in both the current
- ;;; and compiler environments, and that the INCLUDES be the same.
- ;;;
- (defun ctypep (obj type)
- (declare (type ctype type))
- (etypecase type
- ((or numeric-type named-type member-type array-type)
- (values (typep obj (type-specifier type)) t))
- (structure-type
- (if (structurep obj)
- (let* ((name (structure-type-name type))
- (info (info type structure-info name))
- (defined-info (info type defined-structure-info name)))
- (if (and info defined-info
- (equal (c::dd-includes info)
- (c::dd-includes defined-info)))
- (values (typep obj name) t)
- (values nil nil)))
- (values nil t)))
- (union-type
- (dolist (mem (union-type-types type) (values nil t))
- (multiple-value-bind (val win)
- (ctypep obj mem)
- (unless win (return (values nil nil)))
- (when val (return (values t t))))))
- (function-type
- (values (functionp obj) t))
- (unknown-type
- (values nil nil))
- (alien-type-type
- (values (alien-typep obj (alien-type-type-alien-type type)) t))
- (hairy-type
- ;; Now the tricky stuff.
- (let* ((hairy-spec (hairy-type-specifier type))
- (symbol (if (consp hairy-spec) (car hairy-spec) hairy-spec)))
- (ecase symbol
- (and
- (if (atom hairy-spec)
- (values t t)
- (dolist (spec (cdr hairy-spec) (values t t))
- (multiple-value-bind (res win)
- (ctypep obj (specifier-type spec))
- (unless win (return (values nil nil)))
- (unless res (return (values nil t)))))))
- (not
- (multiple-value-bind
- (res win)
- (ctypep obj (specifier-type (cadr hairy-spec)))
- (if win
- (values (not res) t)
- (values nil nil))))
- (satisfies
- (let ((fun (second hairy-spec)))
- (cond ((and (consp fun) (eq (car fun) 'lambda))
- (values (not (null (funcall (coerce fun 'function) obj)))
- t))
- ((and (symbolp fun) (fboundp fun))
- (values (not (null (funcall fun obj))) t))
- (t
- (values nil nil))))))))))
-
-
- ;;; Ctype-Of -- Interface
- ;;;
- ;;; Like Type-Of, only returns a Type structure instead of a type
- ;;; specifier. We try to return the type most useful for type checking, rather
- ;;; than trying to come up with the one that the user might find most
- ;;; informative.
- ;;;
- (proclaim '(function ctype-of (t) ctype))
- (defun-cached (ctype-of
- :hash-function (lambda (x)
- (the fixnum
- (logand (the fixnum (cache-hash-eq x))
- #x1FF)))
- :hash-bits 9)
- ((x eq))
- (typecase x
- (character
- (specifier-type
- (if (standard-char-p x)
- 'standard-char
- 'base-char)))
- (compiled-function (specifier-type 'compiled-function))
- (cons (specifier-type 'cons))
- (symbol
- (make-member-type :members (list x)))
- (number
- (let* ((num (if (complexp x) (realpart x) x))
- (res (make-numeric-type
- :class (etypecase num
- (integer 'integer)
- (rational 'rational)
- (float 'float))
- :format (if (floatp num)
- (float-format-name num)
- nil))))
- (cond ((complexp x)
- (setf (numeric-type-complexp res) :complex)
- (let ((imag (imagpart x)))
- (setf (numeric-type-low res) (min num imag))
- (setf (numeric-type-high res) (max num imag))))
- (t
- (setf (numeric-type-low res) num)
- (setf (numeric-type-high res) num)))
- res))
- (structure
- ;;
- ;; In bootstrapping, there can be an instance of a structure type that
- ;; isn't defined in the current environment.
- (let ((type (type-of x)))
- (if (info type structure-info type)
- (make-structure-type :name type)
- *universal-type*)))
- (array
- (let ((etype (specifier-type (array-element-type x))))
- (make-array-type :dimensions (array-dimensions x)
- :complexp (not (typep x 'simple-array))
- :element-type etype
- :specialized-element-type etype)))
- (t
- *universal-type*)))
-
- ;;; Clear this cache on GC so that we don't hold onto too much garbage.
- ;;;
- (pushnew 'ctype-of-cache-clear *before-gc-hooks*)
-
-
- ;;;; Standard Deftypes.
-
- (deftype atom () '(not cons))
- (deftype list () '(or cons null))
- (deftype null () '(member nil))
- (deftype sequence () '(or vector list))
-
-
- ;;;; Compatibility Deftypes.
-
- ;;; (Array Number) should probably really be (Array Common), but we can't hack
- ;;; circular type definitions. This is probably O.K. since numeric arrays
- ;;; are the only kind of Common array which is likely to exist.
- (deftype common ()
- '(or cons symbol (array t) (array number) string number standard-char structure))
-
- (deftype string-char ()
- 'base-char)
-
-
- ;;;; Some types that we use in defining the standard functions:
- ;;;
-
- ;;;
- ;;; A type specifier.
- (deftype type-specifier () '(or list symbol))
- ;;;
- ;;; An index into an array. Also used for sequence index.
- (deftype index () `(integer 0 (,array-dimension-limit)))
- ;;;
- ;;; Array rank, total size...
- (deftype array-rank () `(integer 0 (,array-rank-limit)))
- (deftype array-total-size () `(integer 0 (,array-total-size-limit)))
- ;;;
- ;;; Some thing legal in an evaluated context.
- (deftype form () t)
- ;;;
- ;;; Maclisp compatibility...
- (deftype stringlike () '(or string symbol))
- (deftype stringable () '(or string symbol character))
- ;;;
- ;;; Save a little typing...
- (deftype truth () '(member t))
- ;;;
- ;;; A thing legal in places where we want the name of a file.
- (deftype filename () '(or string pathname))
- ;;;
- ;;; A legal arg to pathname functions.
- (deftype pathnamelike () '(or string pathname stream))
- ;;;
- ;;; A thing returned by the irrational functions. We assume that they never
- ;;; compute a rational result.
- (deftype irrational () '(or float (complex float)))
- ;;;
- ;;; Character components:
- (deftype char-code () `(integer 0 (,char-code-limit)))
- ;;;
- ;;; A consed sequence result. If a vector, is a simple array.
- (deftype consed-sequence () '(or list (simple-array * (*))))
- ;;;
- ;;; The :end arg to a sequence...
- (deftype sequence-end () '(or null index))
- ;;;
- ;;; A valid argument to a stream function...
- (deftype streamlike () '(or stream (member nil t)))
- ;;;
- ;;; A thing that can be passed to funcall & friends.
- (deftype callable () '(or function symbol))
-
- ;;; Until we decide if and how to wedge this into the type system, make it
- ;;; equivalent to t.
- ;;;
- (deftype void () t)
-
-
- ;;; ### Hack. We need this for at lease a few more iterations.
- (deftype base-character () 'base-char)
-
-
-
- ;;;; Cold loading initializations.
-
- (emit-cold-load-defuns)
-
- (defun type-init ()
- (setf *use-implementation-types* t)
- (setf *type-classes* (make-hash-table :test #'equal))
- (setf *unparse-function-type-simplify* nil)
- (setf *values-type-union-cache-vector*
- (make-array (* (ash 1 8) (+ 2 1))))
- (setf *values-type-intersection-cache-vector*
- (make-array (* (ash 1 8) (+ 2 2))))
- (setf *values-subtypep-cache-vector*
- (make-array (* (ash 1 8) (+ 2 2))))
- (setf *csubtypep-cache-vector*
- (make-array (* (ash 1 8) (+ 2 2))))
- (setf *type=-cache-vector*
- (make-array (* (ash 1 8) (+ 2 2))))
- (setf *type-union-cache-vector*
- (make-array (* (ash 1 8) (+ 2 1))))
- (setf *type-intersection-cache-vector*
- (make-array (* (ash 1 8) (+ 2 2))))
- (setf *values-specifier-type-cache-vector*
- (make-array (* (ash 1 10) (+ 1 1))))
- (setf *ctype-of-cache-vector*
- (make-array (* (ash 1 9) (+ 1 1))))
- (do-cold-load-init-forms)
- nil)
-